package com.opass.cloud.core.security;

import cn.hutool.core.bean.BeanUtil;
import com.opass.cloud.core.security.oauth2.CustomRedisTokenServices;
import com.opass.cloud.core.security.oauth2.CustomJwtAccessTokenEnhancer;
import com.opass.cloud.core.security.oauth2.CustomJwtTokenServices;
import com.opass.cloud.core.security.oauth2.CustomUserAuthenticationConverter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.UnauthorizedUserException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.util.Assert;

import java.util.Collection;
import java.util.Map;

/**
 * 认证信息帮助类
 *
 * @author liuyadu
 */
@Slf4j
public class SecurityHelper {


    /**
     * 获取当前用户ID
     *
     * @return
     */
    public static Long getUserId() {
        SecurityUser securityUser = getCurrent();
        if (securityUser == null) {
            throw new UnauthorizedUserException("未认证");
        }
        if (securityUser.isClientOnly()) {
            throw new UnauthorizedUserException("缺少用户认证信息");
        }
        return securityUser.getUserId();
    }

    /**
     * 获取客户端ID
     *
     * @return
     */
    public static String getClientId() {
        SecurityUser auth = getCurrent();
        if (auth == null) {
            throw new UnauthorizedUserException("未认证");
        }
        return auth.getClientId();
    }


    /**
     * 获取认证信息
     *
     * @return
     */
    public static SecurityUser getCurrent() {
        SecurityUser user = null;
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.isAuthenticated()) {
            Object principal = authentication.getPrincipal();
            if (principal instanceof SecurityUser) {
                user = (SecurityUser) principal;
            }
            if (principal instanceof Map) {
                user = BeanUtil.toBean((Map) authentication.getPrincipal(), SecurityUser.class);
            }
            if (authentication instanceof OAuth2Authentication) {
                OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) authentication;
                OAuth2Request oAuth2Request = oAuth2Authentication.getOAuth2Request();
                if (!oAuth2Authentication.isClientOnly()) {
                    user.setClientId(oAuth2Request.getClientId());
                } else {
                    user.setClientId(oAuth2Request.getClientId());
                    user.setAuthorities(oAuth2Request.getAuthorities());
                }
            }
        }
        return user;
    }


    /**
     * 构建token转换器
     *
     * @return
     */
    public static DefaultAccessTokenConverter buildAccessTokenConverter() {
        CustomUserAuthenticationConverter userAuthenticationConverter = new CustomUserAuthenticationConverter();
        DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
        accessTokenConverter.setUserTokenConverter(userAuthenticationConverter);
        return accessTokenConverter;
    }

    /**
     * 构建jwtToken转换器
     *
     * @return
     */
    public static JwtAccessTokenConverter buildJwtTokenEnhancer(String signingKey) throws Exception {
        JwtAccessTokenConverter converter = new CustomJwtAccessTokenEnhancer();
        converter.setSigningKey(signingKey);
        converter.afterPropertiesSet();
        return converter;
    }


    /**
     * 构建资源服务器JwtToken服务类
     *
     * @return
     */
    public static ResourceServerTokenServices buildJwtTokenServices() throws Exception {
        // 使用自定义系统用户凭证转换器
        DefaultAccessTokenConverter accessTokenConverter = buildAccessTokenConverter();
        CustomJwtTokenServices tokenServices = new CustomJwtTokenServices();
        // 这里的签名key 保持和认证中心一致
        JwtAccessTokenConverter converter = buildJwtTokenEnhancer("2cde1eaa60fe4af1987f94caa13f29a2");
        JwtTokenStore jwtTokenStore = new JwtTokenStore(converter);
        tokenServices.setTokenStore(jwtTokenStore);
        tokenServices.setJwtAccessTokenConverter(converter);
        tokenServices.setDefaultAccessTokenConverter(accessTokenConverter);
        log.info("buildJwtTokenServices[{}]", tokenServices);
        return tokenServices;
    }

    /**
     * 构建资源服务器RedisToken服务类
     *
     * @return
     */
    public static ResourceServerTokenServices buildRedisTokenServices(RedisConnectionFactory redisConnectionFactory) throws Exception {
        CustomRedisTokenServices tokenServices = new CustomRedisTokenServices();
        // 这里的签名key 保持和认证中心一致
        RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);
        tokenServices.setTokenStore(redisTokenStore);
        log.info("buildRedisTokenServices[{}]", tokenServices);
        return tokenServices;
    }

    /**
     * 根据客户端和用户名删除token
     *
     * @param tokenStore
     * @param clientId
     * @param username
     */
    public static void removeTokensByClientIdAndUserName(TokenStore tokenStore, String clientId, String username) {
        Assert.notNull(tokenStore, "tokenStore不能为空");
        Assert.notNull(clientId, "客户端ID不能为空");
        Assert.notNull(username, "用户名不能为空");
        // 动态更新客户端生成的token
        Collection<OAuth2AccessToken> accessTokens = tokenStore.findTokensByClientIdAndUserName(clientId, username);
        if (accessTokens != null && !accessTokens.isEmpty()) {
            for (OAuth2AccessToken accessToken : accessTokens) {
                tokenStore.removeAccessToken(accessToken);
            }
        }
    }
}
